/*
 * SPDX-FileCopyrightText: 2022-2023 乐鑫信息科技（上海）股份有限公司
 *
 * SPDX-License-Identifier: Apache-2.0
 *  SPDX许可证标识，表明本代码遵循Apache 2.0开源协议
 */

#include "esp_err.h"            // ESP32错误处理相关头文件
#include "ir_encoder.h"         // 红外编码器模块头文件，包含相关结构体和函数声明
#include "IRutils.h"         // 红外编码器模块头文件，包含相关结构体和函数声明

// 日志标签，用于打印该模块相关的日志信息
static const char *TAG = "raw_encoder";

/**
 * @brief NEC红外编码器结构体
 * 封装了NEC协议红外编码器的相关资源和状态，继承自RMT编码器基础接口
 */
typedef struct {
    rmt_encoder_t base;                   // 基础编码器接口，定义了标准的编码器操作方法
    rmt_encoder_t *copy_encoder;          // 复制编码器，用于编码前导码和结束码等脉冲信号
    rmt_encoder_t *bytes_encoder;         // 字节编码器，用于编码地址和命令等数据（当前未使用但预留）
    rmt_symbol_word_t nec_leading_symbol; // NEC协议前导码的RMT表示形式
    rmt_symbol_word_t nec_ending_symbol;  // NEC协议结束码的RMT表示形式
    int state;                            // 编码器状态，用于多阶段编码的状态切换
} rmt_ir_nec_encoder_t;

/**
 * @brief RMT原始红外数据编码函数
 * 将原始红外数据编码为RMT符号，用于通过RMT通道发送
 * @param encoder 编码器实例
 * @param channel RMT通道句柄
 * @param primary_data 原始红外数据（rmt_symbol_word_t类型数组）
 * @param data_size 原始数据的元素数量（而非字节数）
 * @param ret_state 编码状态输出（完成/内存不足等）
 * @return 已编码的符号数量
 */
static size_t ir_encoder_rmt_raw(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
    // 检查编码器有效性，无效则返回错误（通过宏定义实现）
    IR_LEARN_CHECK(encoder, "invalid argument", ESP_ERR_INVALID_ARG);

    // 通过基础接口指针获取结构体实例（容器指针转换）
    rmt_ir_nec_encoder_t *nec_encoder = __containerof(encoder, rmt_ir_nec_encoder_t, base);
    rmt_encode_state_t session_state = 0;                          // 当前编码会话状态
    rmt_encode_state_t state = 0;                                  // 总编码状态
    size_t encoded_symbols = 0;                                    // 已编码的符号数量
    rmt_encoder_handle_t copy_encoder = nec_encoder->copy_encoder; // 获取复制编码器

    // 使用复制编码器编码原始数据（数据大小需转换为字节数）
    encoded_symbols += copy_encoder->encode(copy_encoder, channel, primary_data, data_size * sizeof(rmt_symbol_word_t), &session_state);
    // 如果当前编码会话完成，更新编码器状态为1（进入下一阶段）
    if (session_state & RMT_ENCODING_COMPLETE) {
        nec_encoder->state = 1; // 仅当当前编码器完成后才切换到下一状态
    }
    // 如果编码过程中内存不足，标记状态并退出
    if (session_state & RMT_ENCODING_MEM_FULL) {
        state |= RMT_ENCODING_MEM_FULL;
        goto out; // 若没有足够空间存放编码结果，则退出
    }
out:
    *ret_state = state;     // 输出编码状态
    return encoded_symbols; // 返回已编码的符号数量
}

/**
 * @brief 销毁红外编码器
 * 释放编码器及其内部资源（子编码器、内存等）
 * @param encoder 编码器实例
 * @return 成功返回ESP_OK，失败返回对应错误码
 */
static esp_err_t ir_encoder_del(rmt_encoder_t *encoder)
{
    // 检查编码器有效性
    IR_LEARN_CHECK(encoder, "invalid argument", ESP_ERR_INVALID_ARG);

    // 获取结构体实例
    rmt_ir_nec_encoder_t *nec_encoder = __containerof(encoder, rmt_ir_nec_encoder_t, base);
    // 释放复制编码器
    if (nec_encoder->copy_encoder) {
        rmt_del_encoder(nec_encoder->copy_encoder);
    }
    // 释放字节编码器（预留）
    if (nec_encoder->bytes_encoder) {
        rmt_del_encoder(nec_encoder->bytes_encoder);
    }
    // 释放编码器结构体内存
    free(nec_encoder);
    return ESP_OK;
}

/**
 * @brief 重置红外编码器状态
 * 重置编码器内部状态及子编码器，用于重新开始编码
 * @param encoder 编码器实例
 * @return 成功返回ESP_OK，失败返回对应错误码
 */
static esp_err_t ir_encoder_reset(rmt_encoder_t *encoder)
{
    // 检查编码器有效性
    IR_LEARN_CHECK(encoder, "invalid argument", ESP_ERR_INVALID_ARG);

    // 获取结构体实例
    rmt_ir_nec_encoder_t *nec_encoder = __containerof(encoder, rmt_ir_nec_encoder_t, base);
    // 重置复制编码器
    if (nec_encoder->copy_encoder) {
        rmt_encoder_reset(nec_encoder->copy_encoder);
    }
    // 重置字节编码器（预留）
    if (nec_encoder->bytes_encoder) {
        rmt_encoder_reset(nec_encoder->bytes_encoder);
    }
    // 重置状态为初始值
    nec_encoder->state = 0;
    return ESP_OK;
}

/**
 * @brief 创建新的红外编码器实例
 * 初始化编码器资源，创建子编码器，返回编码器句柄
 * @param config 编码器配置参数
 * @param ret_encoder 输出参数，用于返回创建的编码器句柄
 * @return 成功返回ESP_OK，失败返回对应错误码
 */
esp_err_t ir_encoder_new(const ir_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
{
    esp_err_t ret = ESP_OK;                   // 函数返回值
    rmt_ir_nec_encoder_t *nec_encoder = NULL; // 编码器实例指针

    // 检查输入参数有效性（配置和输出句柄不能为空）
    IR_LEARN_CHECK(config && ret_encoder, "invalid argument", ESP_ERR_INVALID_ARG);

    // 分配编码器结构体内存（初始化为0）
    nec_encoder = calloc(1, sizeof(rmt_ir_nec_encoder_t));
    IR_LEARN_CHECK(nec_encoder, "no mem for ir nec encoder", ESP_ERR_NO_MEM);

    // 初始化基础编码器接口的函数指针
    nec_encoder->base.encode = ir_encoder_rmt_raw; // 绑定编码函数
    nec_encoder->base.del = ir_encoder_del;        // 绑定销毁函数
    nec_encoder->base.reset = ir_encoder_reset;    // 绑定重置函数

    // 配置并创建复制编码器
    rmt_copy_encoder_config_t copy_encoder_config = {}; // 复制编码器配置（默认初始化）
    ret = rmt_new_copy_encoder(&copy_encoder_config, &nec_encoder->copy_encoder);
    // 若创建复制编码器失败，跳转到错误处理
    IR_LEARN_CHECK_GOTO((ESP_OK == ret), "create copy encoder failed", ESP_FAIL, err);

    // 输出编码器句柄（返回基础接口指针）
    *ret_encoder = &nec_encoder->base;
    return ESP_OK;

// 错误处理标签：释放已分配的资源
err:
    if (nec_encoder) {
        // 释放字节编码器（若已创建）
        if (nec_encoder->bytes_encoder) {
            rmt_del_encoder(nec_encoder->bytes_encoder);
        }
        // 释放复制编码器（若已创建）
        if (nec_encoder->copy_encoder) {
            rmt_del_encoder(nec_encoder->copy_encoder);
        }
        // 释放编码器结构体内存
        free(nec_encoder);
    }
    return ret; // 返回错误码
}